package ctrutil

import (
	"cloud_worm/util/unet/rpc"
	"sync"
	"io"
	"fmt"
	"github.com/astaxie/beego/logs"
	"net"
	"time"
	"crypto/tls"
)

// RpcClientPool.Call 远程调用，同步方式
func (r *RpcClientPool) Call(addrIndex string, funcName string, args interface{}, reply interface{}) error {
	r.Lock()
	defer r.Unlock()

	if _, exist := r.Servers[addrIndex]; exist != true {
		err := r.newClient(addrIndex)
		if err != nil {
			return err
		}
	}

	//select {
	//case call := <- r.Servers[addrIndex].Go(funcName, args, reply, make(chan *rpc.Call, 1)).Done:
	//	if call.Error != nil {
	//		if call.Error == rpc.ErrShutdown || call.Error == io.ErrUnexpectedEOF {
	//			err := r.newClient(addrIndex)
	//			if err != nil {
	//				return err
	//			}
	//
	//			// 重连之后，立马调用直接采用Call
	//			err = r.Servers[addrIndex].Call(funcName, args, reply)
	//			return err
	//		}
	//
	//		return call.Error
	//	}
	//
	//case <- time.After(time.Second * 5):
	//	logs.Error(fmt.Sprintf("Method: %s, Args: %v, Error: call timeout 10s", funcName, args))
	//	err := r.newClient(addrIndex)
	//	if err != nil {
	//		return err
	//	}
	//
	//	// 重连之后，立马调用直接采用Call
	//	err = r.Servers[addrIndex].Call(funcName, args, reply)
	//	return err
	//}

	err := r.Servers[addrIndex].Call(funcName, args, reply)
	if err != nil {
		if err == rpc.ErrShutdown || err == io.ErrUnexpectedEOF {
			err := r.newClient(addrIndex)
			if err != nil {
				return err
			}

			err = r.Servers[addrIndex].Call(funcName, args, reply)
			return err
		}

		return err
	}

	return nil
}

// RpcClientPool.connect 执行连接
// 非并发安全
func (r *RpcClientPool) newClient(addr string) error {
	// 尝试回收资源
	if _, exist := r.Servers[addr]; exist == true {
		r.Servers[addr].Close()
		delete(r.Servers, addr)
	}

	// 重连
	config := &tls.Config{InsecureSkipVerify: true}
	d := &net.Dialer{Timeout: 3 * time.Second}
	conn, err := tls.DialWithDialer(d, "tcp", addr, config)
	//conn, err := net.DialTimeout("tcp", addr, 3 * time.Second)
	if err != nil {
		return err
	}
	logs.Warn(fmt.Sprintf("rpc client: %s reconnect", addr))

	option := rpc.ClientOption{
		Debug: true,
		ReadTimeout: 30 * time.Second,
		WriteTimeout: 5 * time.Second,
		HeartbeatInterval: 20 * time.Second,
	}
	client := rpc.NewClient(conn, option)
	r.Servers[addr] = client
	return nil
}

// RpcServerPool 远程rpc server的池，当下发task时，需要机遇agent对应的 AccessSrv 执行下发
type RpcClientPool struct {
	sync.Mutex						// protect Servers, client config Goroutine write; other Goroutine read
	Servers 		map[string]*rpc.Client
}

// AccessSrvRpc 全局变量
var AccessSrvRpc = &RpcClientPool{
	Servers: make(map[string]*rpc.Client),
}
